package com.topnav.mc.admin.controller;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.js.lib.cache.SysCache;
import com.js.lib.result.Result;
import com.js.lib.utils.DateUtils;
import com.js.lib.utils.JsonUtil;
import com.topnav.mc.admin.entity.Device;
import com.topnav.mc.admin.entity.GbServer;
import com.topnav.mc.admin.entity.bean.DeviceBean;
import com.topnav.mc.admin.entity.param.DeviceParam;
import com.topnav.mc.admin.service.IDeviceChannelService;
import com.topnav.mc.admin.service.IDeviceService;
import com.topnav.mc.admin.service.IGbServerService;
import com.topnav.mc.exception.ExceptionMsg;
import com.topnav.mc.gbserverapi.GbServerApi;
import com.topnav.mc.involve.http.HttpClientUtil;
import com.topnav.mc.involve.http.HttpUtil;
import com.topnav.mc.uitls.GbServerUtil;
import com.topnav.mc.uitls.ResultErrorUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import com.topnav.mc.common.Constant;
import javax.annotation.Resource;
import java.net.ConnectException;
import java.util.Date;
import java.util.Map;

import static com.topnav.mc.exception.ExceptionMsg.*;

/**
 * 国标设备播放
 */
@Api(value = "/play", tags = "监控播放(直播拉流)")
@CrossOrigin
@RestController
@RequestMapping("/play")
@Slf4j
public class GbPlayerController {
    @Autowired
    private IDeviceChannelService deviceChannelService;
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private IGbServerService gbServerService;
    @Autowired
    private SysCache sysCache;
    @Resource
    private GbServerApi api;

    @ApiOperation("取得播放地址，指定为WS类型")
    @ApiImplicitParams(
            {@ApiImplicitParam(name="deviceCode", value = "设备编号", required=false,dataTypeClass = String.class),
             @ApiImplicitParam(name="channelCode", value = "通道编号", required=false,dataTypeClass = String.class)})
    @GetMapping("/getUrl/{deviceCode}/{channelCode}")
    public Result getUrl(@PathVariable String deviceCode,@PathVariable String channelCode){
        return null;
    }


    /**
     * 取得截图，实现拍照功能
     * @param deviceCode
     * @param channelCode
     * @return
     */
    @GetMapping("/getSnap/{deviceCode}/{channelCode}")
    public  DeferredResult<Result> getSnap(@PathVariable String deviceCode,@PathVariable String channelCode){
        DeferredResult<Result> result = new DeferredResult<>(6*1000L);
        result.onTimeout(() -> {
            //服务端超时未响应
            log.error(String.format(">>>>>> %s",ERROR_SERVER_TIMEOUT_NOT_RESPONED.getDesc()));
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_TIMEOUT_NOT_RESPONED));
        });
        Device device = deviceService.getFromCache(deviceCode);
        if (device == null) {
            //不存在的设备编号
            log.error(String.format(">>>>>> %s",ERROR_NOT_EXISTS_EXCEPTION.getDesc()));
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_EXISTS_EXCEPTION));
            return result;
        }

        String url=null;
        if (GbServerUtil.get() != null){
            url = String.format("http://%s:%s/api/play/snap?deviceId=%s&channelId=%s",
                    GbServerUtil.getIp(),GbServerUtil.getPort(),deviceCode,channelCode
            );
        }else{
            if (device.getGbServerId()==null){
                //设备没有绑定服务器

                result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_BIND_SERVER_EXISTS));
                return result;
            }
            GbServer gbServer = gbServerService.getFromCache(device.getGbServerId().toString());
            if (gbServer==null){
                //不存在的服务器
                result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_NS_SERVER_EXISTS_EXCEPTION));
                return result;
            }
            StringBuffer sb = new StringBuffer();
            //http://192.168.0.129:18080/api/play/snap?deviceId=44010200492000000020&channelId=44030000492000000020
            url = String.format("http://%s:%s/api/play/snap?deviceId=%s&channelId=%s",
                    gbServer.getHost(),
                    gbServer.getApiPort(),
                    deviceCode,
                    channelCode
            );
        }
        final String url1 = url;

        new Thread(()->{
            HttpClientUtil.get(url1, 5*1000,
                    null,
                    new FutureCallback<HttpResponse>() {
                        @SneakyThrows
                        @Override
                        public void completed(HttpResponse response) {
                            HttpEntity entity = response.getEntity();
                            String res = EntityUtils.toString(entity);
                            System.out.println("远程API 响应--->" + res);
                            JSONObject json = JSONObject.parseObject(res);
                            if (json.getInteger("code") != 0){
                                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                result.setResult(
                                        ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code")))
                                                 );
                            }else{

                                result.setResult(Result.ok(json.getJSONObject("data")));
                            }
                        }
                        @Override
                        public void failed(Exception e) {
                            log.error(e.toString());
                            if (e instanceof ConnectException){
                                //无法连接国标服务器
                                log.error(String.format(">>>>>> %s",ERROR_SERVER_UNABLED_TO_CONNECT_NS_SERVER.getDesc()));
                                result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_UNABLED_TO_CONNECT_NS_SERVER));
                            }else{
                                log.error(String.format(">>>>>> %s",e.toString()));
                                result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,e.toString()));
                            }
                        }
                        @Override
                        public void cancelled() {
                            log.error("操作已被用户取消！");
                            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_OPERATION_HAS_BEEN_CANCELS_BY_USER));
                        }
                    },
                    (err)->{
                        log.error(String.format(">>>>>> %s",err.toString()));
                        result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,err.toString()));
                    }
            );
        }).start();

        return result;
    }
    /**
     * 申请推流
     * @param deviceCode
     * @param channelCode
     * @return
     */
    @ApiImplicitParams(
            {@ApiImplicitParam(name="deviceCode", value = "设备编号", required=false,dataTypeClass = String.class),
                    @ApiImplicitParam(name="channelCode", value = "通道编号", required=false,dataTypeClass = String.class)})
    @GetMapping("/playstart/{deviceCode}/{channelCode}")
    public DeferredResult<Result> playstart(@PathVariable String deviceCode,@PathVariable String channelCode){
        System.err.println(String.format("%s/%s",deviceCode,channelCode));
        DeferredResult<Result> result = new DeferredResult<>((Constant.PLAY_TIMEOUT + 1)*1000L);
        result.onTimeout(() -> {
            //服务端超时未响应
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_TIMEOUT_NOT_RESPONED));
            return;
        });

        Device device = deviceService.getFromCache(deviceCode);
        if (device == null){
            //不存在的设备编号
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_EXISTS_EXCEPTION));
            return result;
        }
        String url = null;
        if (GbServerUtil.get() != null){
            url = String.format("http://%s:%s/api/play/start/%s/%s",
                    GbServerUtil.getIp(),GbServerUtil.getPort(),deviceCode,channelCode
            );
        }else{
            if (device.getGbServerId()==null){
                //设备没有绑定服务器
                result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_BIND_SERVER_EXISTS));
                return result;
            }
            GbServer gbServer = gbServerService.getFromCache(device.getGbServerId().toString());
            if (gbServer==null){
                //不存在的服务器
                result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_NOT_NS_SERVER_EXISTS_EXCEPTION));
                return result;
            }
            StringBuffer sb = new StringBuffer();
            //http://192.168.0.129:18080/apiapi/play/start/44010200492000000020/44030000492000000020
            url = String.format("http://%s:%s/api/play/start/%s/%s",
                    gbServer.getHost(),
                    gbServer.getApiPort(),
                    deviceCode,
                    channelCode
            );
        }
        final String url1=url;

        new Thread(()->{
            HttpClientUtil.get(url1, (Constant.PLAY_TIMEOUT+2)*1000,
                    null,
                    new FutureCallback<HttpResponse>() {
                        @SneakyThrows
                        @Override
                        public void completed(HttpResponse response) {
                            HttpEntity entity = response.getEntity();
                            String res = EntityUtils.toString(entity);
                            System.out.println("远程API 响应--->" + res);
                            JSONObject json = JSONObject.parseObject(res);
                            if (json.getInteger("code") != 0){
                                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                result.setResult(ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code"),json.getString("msg"))));
                            }else{
                                result.setResult(Result.ok(json.getJSONObject("data")));
                            }
                        }
                        @Override
                        public void failed(Exception e) {
                            log.error(e.toString());
                           if (e instanceof ConnectException){
                               //无法连接国标服务器
                               log.error(String.format(">>>>>>>> %s",ERROR_SERVER_UNABLED_TO_CONNECT_NS_SERVER.getDesc()));
                               result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_UNABLED_TO_CONNECT_NS_SERVER));
                           }else{
                               log.error(String.format(">>>>>>>> %s",e.toString()));
                               result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,e.toString()));
                           }
                        }
                        @Override
                        public void cancelled() {
                            log.error("操作已被用户取消！");
                            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_OPERATION_HAS_BEEN_CANCELS_BY_USER));
                        }
                    },
                    (err)->{
                        log.error(String.format(">>>>>>>> %s",err.toString()));
                        result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,err.toString()));
                    }
            );
        }).start();

        return result;
    }

    @Deprecated
    @ApiOperation("取得播放地址，指定为WS类型")
    @ApiImplicitParams(
            {@ApiImplicitParam(name="deviceCode", value = "设备编号", required=false,dataTypeClass = String.class),
             @ApiImplicitParam(name="channelCode", value = "通道编号", required=false,dataTypeClass = String.class)})
    @GetMapping("/temp/playstart/{deviceCode}/{channelCode}")
    public Result playstart_back(@PathVariable String deviceCode,@PathVariable String channelCode){
        System.err.println(String.format("%s/%s",deviceCode,channelCode));

        QueryWrapper<DeviceParam> wrapper = new QueryWrapper<>();
        wrapper.eq("d.code",deviceCode);
        DeviceBean deviceBean = deviceService.getOneInfo(wrapper,false);
        String url = null;
        if (GbServerUtil.get() != null){
            url=String.format("http://%s:%s",GbServerUtil.getIp(),GbServerUtil.getPort());
        }else{
            url=String.format("http://%s:%s",deviceBean.getGbServerHost(),deviceBean.getGbServerApiPort());
        }

        try{
            String apiResult = api.playStart(url,deviceCode,channelCode);
            JSONObject json = JSONObject.parseObject(apiResult);
            if (Integer.valueOf(json.get("code").toString()) != 0){
                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                return ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code"),json.getString("msg")));

            }
            Map<Object,Object> data = JsonUtil.fromJson(json.get("data").toString(),Map.class);
            return Result.ok(data);
        }catch (Exception e){
            log.error(">>>>>> 取拉流地址错误: {}",e.toString());
            return ResultErrorUtil.byExceptionMsg(ERROR_REAL_TIME_ATREAM_ON_DEMAND_FAILURE);
        }
    }

    /**
     * 取服务器的 参数
     * @param deviceCode
     * @return
     */
    @GetMapping("/get/config/{deviceCode}")
    public Result getConfig(@PathVariable String deviceCode){
        QueryWrapper<DeviceParam> wrapper = new QueryWrapper<>();
        wrapper.eq("d.code",deviceCode);
        DeviceBean deviceBean = deviceService.getOneInfo(wrapper,false);
        String url = null;
        if (GbServerUtil.get() != null){
            url = String.format("http://%s:%s",GbServerUtil.getIp(),GbServerUtil.getPort());
        }else{
            url = String.format("http://%s:%s",deviceBean.getGbServerHost(),deviceBean.getGbServerApiPort());
        }

        try{
            String apiResult = api.getConfig(url);
            JSONObject json = JSONObject.parseObject(apiResult);
            if (Integer.valueOf(json.get("code").toString()) != 0){
                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                return ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code"),json.getString("msg")));
            }
            Map<Object,Object> data = JsonUtil.fromJson(json.get("data").toString(),Map.class);
            return Result.ok(data);
        }catch (Exception e){
            log.error(">>>>>> 取国标服务器的参数失败: {}",e.toString());
            return ResultErrorUtil.byExceptionMsg(ERROR_GET_PARAM_FROM_NS_SErVER_FAILURE);

        }
    }

    /**
     * 国标语音对讲
     * @param deviceCode
     * @param app
     * @param stream
     * @return
     */
    @GetMapping("/start/gbtalk/{deviceCode}/{app}/{stream}")
    public DeferredResult<Result> playGbTalk(@PathVariable String deviceCode,@PathVariable String app,@PathVariable String stream){
        DeferredResult<Result> result = new DeferredResult<Result>(5L*1000);
        //System.out.println(DateUtils.format(new Date(),"HH:mm:ss.SSS"));
        result.onTimeout(() -> {
            //服务端超时未响应
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_TIMEOUT_NOT_RESPONED));
        });
        Device device = deviceService.getFromCache(deviceCode);
        String url = null;
        if (GbServerUtil.get() != null){
            url = String.format("http://%s:%s/api/play/broadcast/start/%s?app=%s&streamId=%s",
                    GbServerUtil.getIp(),GbServerUtil.getPort(),deviceCode,app,stream);
        }else{
            GbServer gbServer = gbServerService.getFromCache(device.getGbServerId().toString());
            StringBuffer sb = new StringBuffer();
            //http://192.168.0.129:18080/api/play/broadcast/start/44010200492000000020?app=live&streamId=44010200492000000020_44030000492000000020_audio
            url = String.format("http://%s:%s/api/play/broadcast/start/%s?app=%s&streamId=%s",
                    gbServer.getHost(),
                    gbServer.getApiPort(),
                    deviceCode,
                    app,
                    stream
            );
        }
        final String url1 = url;
        //学到了，DeferredResult 在同一个线程里，如果有thread.sleep, TimeOut是不起作用的
        new Thread(()->{
            HttpClientUtil.get(url1, 5*1000,
                    null,
                    new FutureCallback<HttpResponse>() {
                        @SneakyThrows
                        @Override
                        public void completed(HttpResponse response) {
                            HttpEntity entity = response.getEntity();
                            String res = EntityUtils.toString(entity);
                            System.out.println("远程API 响应--->" + res);
                            JSONObject json = JSONObject.parseObject(res);
                            if (json.getJSONObject("data")!=null){
                                JSONObject data = json.getJSONObject("data");
                                if (data.getInteger("code") != 0){
                                    log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                    result.setResult(ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code"),json.getString("msg"))));
                                }else{
                                    result.setResult(Result.ok("语音广播发送成功"));
                                }
                            }else{
                                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,json.getString("msg")));
                            }
                        }
                        @Override
                        public void failed(Exception e) {
                            log.error(String.format(">>>>>>>> %s",e.toString()));
                            result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,e.toString()));
                        }
                        @Override
                        public void cancelled() {
                            log.error("操作已被用户取消！");
                            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_OPERATION_HAS_BEEN_CANCELS_BY_USER));
                        }
                    },
                    (err)->{
                        log.error(String.format(">>>>>>>> %s",err.toString()));
                        result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,err.toString()));
                    }
            );
        }).start();
       return result;
    }

    @GetMapping("/stop/gbtalk/{deviceCode}/{app}/{stream}")
    public DeferredResult<Result> stopGbTalk(@PathVariable String deviceCode,@PathVariable String app,@PathVariable String stream) {
        DeferredResult<Result> result = new DeferredResult<Result>(5L * 1000);
        result.onTimeout(() -> {
            //服务端超时未响应
            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_SERVER_TIMEOUT_NOT_RESPONED));
        });
        Device device = deviceService.getFromCache(deviceCode);
        String url = null;
        if (GbServerUtil.get() != null){
            url = String.format("http://%s:%s/api/play/broadcast/stop/%s",
                    GbServerUtil.getIp(),GbServerUtil.getPort(),deviceCode);
        }else {
            GbServer gbServer = gbServerService.getFromCache(device.getGbServerId().toString());
            StringBuffer sb = new StringBuffer();
            //http://192.168.0.129:18080/api/play/broadcast/stop/44010200492000000020
            url = String.format("http://%s:%s/api/play/broadcast/stop/%s",
                    gbServer.getHost(),
                    gbServer.getApiPort(),
                    deviceCode
            );
        }
        final String url1=url;

        //学到了，DeferredResult 在同一个线程里，如果有thread.sleep, TimeOut是不起作用的
        new Thread(() -> {
            HttpClientUtil.get(url1, 5*1000,
                    null,
                    new FutureCallback<HttpResponse>() {
                        @SneakyThrows
                        @Override
                        public void completed(HttpResponse response) {
                            /* {"code": 0,"data": {"msg": "不存在要关闭的语音流","code": -1}, "msg": "成功"}  */
                            HttpEntity entity = response.getEntity();
                            String res = EntityUtils.toString(entity);
                            System.out.println("远程API 响应--->" + res);
                            JSONObject json = JSONObject.parseObject(res);
                            if (json.getJSONObject("data") != null) {
                                JSONObject data = json.getJSONObject("data");
                                if (data.getInteger("code") != 0) {
                                    log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                    result.setResult(ResultErrorUtil.byExceptionMsg(ExceptionMsg.getWvpException(json.getInteger("code"),json.getString("msg"))));

                                } else {
                                    result.setResult(Result.ok("语音广播发送成功"));
                                }
                            } else {
                                log.error(String.format(">>>>>>>> %s",json.getString("msg")));
                                result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,json.getString("msg")));
                            }
                        }

                        @Override
                        public void failed(Exception e) {
                            log.error(String.format(">>>>>>>> %s",e.toString()));
                            result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,e.toString()));
                        }

                        @Override
                        public void cancelled() {
                            log.error("操作已被用户取消！");
                            result.setResult(ResultErrorUtil.byExceptionMsg(ERROR_OPERATION_HAS_BEEN_CANCELS_BY_USER));
                        }
                    },
                    (err) -> {
                        log.error(String.format(">>>>>>>> %s",err.toString()));
                        result.setResult(ResultErrorUtil.byExceptionMsg(EXCEPTION,err.toString()));
                    });
        }).start();
        return result;
    }

    @GetMapping("/test/deferredresult")
    public DeferredResult<Result> playInvite() throws InterruptedException {
        String start = DateUtils.format(new Date(),"HH:mm:ss.SSS");
        DeferredResult<Result> result = new DeferredResult<Result>(5000L);
        result.onTimeout(()->{
            log.error(String.format("time out: %s==>%s",start,DateUtils.format(new Date(),"HH:mm:ss.SSS")));
            result.setResult(Result.error(String.format("time out: %s==>%s",start,DateUtils.format(new Date(),"HH:mm:ss.SSS"))));
        });

        new Thread(()->{
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        ;

        return result;
    }
}



/** {"code": -1,"msg": "收流超时，请稍候重试","data": null}*/
/**
 {
 "code": 0,
 "msg": "success",
 "data": {
 "app": "rtp",
 "streamId": "44000000491327000001_44000000491327000001",
 "deviceID": "44000000491327000001",
 "channelId": "44000000491327000001",
 "flv": "http://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.flv",
 "https_flv": "https://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.flv",
 "ws_flv": "ws://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.flv",
 "wss_flv": "wss://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.flv",
 "fmp4": "http://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.live.mp4",
 "https_fmp4": "https://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.live.mp4",
 "ws_fmp4": "ws://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.live.mp4",
 "wss_fmp4": "wss://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.live.mp4",
 "hls": "http://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001/hls.m3u8",
 "https_hls": "https://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001/hls.m3u8",
 "ws_hls": "ws://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001/hls.m3u8",
 "wss_hls": "wss://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001/hls.m3u8",
 "ts": "http://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.live.ts",
 "https_ts": "https://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.live.ts",
 "ws_ts": "ws://106.52.41.35:8080/rtp/44000000491327000001_44000000491327000001.live.ts",
 "wss_ts": "wss://106.52.41.35:443/rtp/44000000491327000001_44000000491327000001.live.ts",
 "rtmp": "rtmp://106.52.41.35:1935/rtp/44000000491327000001_44000000491327000001",
 "rtmps": null,
 "rtsp": "rtsp://106.52.41.35:554/rtp/44000000491327000001_44000000491327000001",
 "rtsps": null,
 "rtc": "https://106.52.41.35:443/index/api/webrtc?app=rtp&stream=44000000491327000001_44000000491327000001&type=play",
 "mediaServerId": "your_server_id",
 "tracks": null,
 "transactionInfo": null
 }
 }
 */
